{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Agent example in LangChain"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Installation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install langchain_google_vertexai langraph"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's explore how to build an agent using LangChain with a practical example. Our goal is to create an agent capable of managing a user's bank accounts. This agent should be able to retrieve the balance of each account and execute transactions between them.\n",
    "\n",
    "While in a production environment we would interact with dedicated banking systems via APIs, let's craft a simple mock framework for our example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pydantic import BaseModel\n",
    "\n",
    "\n",
    "class Account(BaseModel):\n",
    "    \"\"\"Represents a bank account with a name and balance.\"\"\"\n",
    "    name: str\n",
    "    balance: float\n",
    "\n",
    "class UserAccounts(BaseModel):\n",
    "    \"\"\"Represents a collection of bank accounts for a user.\"\"\"\n",
    "\n",
    "    accounts: list[Account]\n",
    "\n",
    "    def get_account_names(self) -> list[str]:\n",
    "        \"\"\"Returns a list of the names of all accounts.\"\"\"\n",
    "        return [account.name for account in self.accounts]\n",
    "\n",
    "    def get_account(self, account_name: str) -> Account:\n",
    "        \"\"\"Returns the account with the given name.\n",
    "\n",
    "        Raises:\n",
    "            ValueError: If no account with the given name exists.\n",
    "        \"\"\"\n",
    "        for account in self.accounts:\n",
    "            if account_name == account.name:\n",
    "                return account\n",
    "        error_message = (\n",
    "            f\"There is no account named {account_name}. \"\n",
    "            f\"Options are {', '.join(self.get_account_names())}\"\n",
    "        )\n",
    "        raise ValueError(error_message)\n",
    "    \n",
    "    def transfer_money(self, amount: float, source_acc_name: str, dest_acc_name: str) -> None:\n",
    "        \"\"\"Transfers money from one account to another.\n",
    "\n",
    "        Raises:\n",
    "            ValueError: If the source account does not have enough funds.\n",
    "        \"\"\"\n",
    "        source_account = self.get_account(source_acc_name)\n",
    "        destination_account = self.get_account(dest_acc_name)\n",
    "        \n",
    "        if source_account.balance < amount:\n",
    "            error_message = (\n",
    "                f\"There is not enough funds in account {source_acc_name}. \"\n",
    "                f\"The balance is only ${source_account.balance}.\"\n",
    "            )\n",
    "            raise ValueError(error_message)\n",
    "        \n",
    "        source_account.balance = source_account.balance - amount\n",
    "        destination_account.balance = destination_account.balance + amount"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This implementation exposes three methods:\n",
    "\n",
    "`get_account_names`: This method returns a list of the names of the accounts associated with the user.\n",
    "\n",
    "`get_account`: This method retrieves a specific account by its name. If the account doesn't exist, it raises a `ValueError` with a clear message indicating the issue and available account options.\n",
    "\n",
    "`transfer_money`: This method allows transferring funds between accounts. It takes the amount, source account name, and destination account name as arguments. It includes error handling to ensure sufficient funds in the source account before executing the transfer.\n",
    "\n",
    "Let's use this implementation to define the tools for the agent."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Tools"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To define the tools available to our agent effectively, we'll employ a decorator and create a global `UserAccounts` object holding user account information as a practical example. As discussed in the preceding chapter, comprehensive docstrings are essential, providing the LLM with the context necessary to determine the appropriate tool to call in any given situation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.tools import tool\n",
    "\n",
    "USER_ACCOUNTS = UserAccounts(\n",
    "    accounts=[\n",
    "        Account(name=\"checking-account\", balance=100),\n",
    "        Account(name=\"savings-account\", balance=3_000)\n",
    "    ]\n",
    ")\n",
    "\n",
    "@tool\n",
    "def list_accounts() -> str:\n",
    "    \"\"\"List the names of the user's accounts.\"\"\"\n",
    "    return USER_ACCOUNTS.get_account_names()\n",
    "\n",
    "@tool\n",
    "def get_account_balance(account_name: str) -> str:\n",
    "    \"\"\"Get the balance of one of the user accounts by its exact name.\"\"\" \n",
    "    try:\n",
    "        account = USER_ACCOUNTS.get_account(account_name)\n",
    "        return f\"${account.balance}\"\n",
    "    except ValueError as error:\n",
    "        return f\"{error}\"\n",
    "\n",
    "\n",
    "@tool\n",
    "def transfer_money(amount: float, source_account: str, destination_account: str) -> str:\n",
    "    \"\"\"Transfer money between two accounts.\"\"\"\n",
    "    try:\n",
    "        USER_ACCOUNTS.transfer_money(amount, source_account, destination_account)\n",
    "        return \"Successful transaction\"\n",
    "    except ValueError as error:\n",
    "        return f\"{error}\"\n",
    "\n",
    "tools = [\n",
    "    list_accounts, \n",
    "    get_account_balance,\n",
    "    transfer_money\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Having defined our toolkit, we will next specify the model responsible for orchestrating their use. This model will  select the most suitable tool for each interaction and generate contextually relevant responses."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For that, we instantiate a `ChatVertexAI` object, which provides an interface to the Gemini 1.5 Pro model. We set the temperature parameter to 0 to encourage the model to produce more focused and consistent responses, which is generally beneficial when working with agents that rely on function calling. Remember that you can further customize this instantiation with additional parameters to fine-tune the model's behavior according to your specific requirements such as the safety settings or the maximum number of output tokens."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_google_vertexai import ChatVertexAI\n",
    "\n",
    "MODEL_NAME = \"gemini-1.5-pro-002\"\n",
    "\n",
    "model = ChatVertexAI(\n",
    "  model_name=MODEL_NAME, \n",
    "  temperature = 0\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now try to refine the behaviour of the model by creating a prompt for it."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Prompt template"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, let's refine the model's behavior by designing an effective prompt. We'll utilize the ChatPromptTemplate class for this purpose. This class requires two components:\n",
    "\n",
    "- `SystemPromptTemplate`: This component contains the general instructions for the model, guiding its overall behavior. For this particular example a `SystetPrompt` would be enough because we don't have any template parameters, but is a good idea to have it in place in case we want to include some later on.\n",
    "  \n",
    "- `MessagesPlaceholder`: This component includes a list of messages, encompassing both the conversation history and the user's current query.\n",
    "  \n",
    "Let's examine the code snippet below to see how this can be implemented:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import (\n",
    "  ChatPromptTemplate, \n",
    "  MessagesPlaceholder, \n",
    "  SystemMessagePromptTemplate, \n",
    ")\n",
    "\n",
    "prompt_template = ChatPromptTemplate.from_messages([\n",
    "  SystemMessagePromptTemplate.from_template(\n",
    "    \"\"\"\n",
    "    You are an agent that helps the user manage their accounts in a Bank.\n",
    "    \n",
    "    Users may not refer to their account by the exact name, so try to get a list of valid names\n",
    "    before getting a balance or executing a transaction.\n",
    "    \"\"\"\n",
    "  ),\n",
    "  MessagesPlaceholder(\n",
    "    variable_name=\"messages\", \n",
    "    optional=True\n",
    "  )\n",
    "])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The core of this system prompt lies in the message provided within the `SystemMessagePromptTemplate`. \n",
    "\n",
    "This message instructs the language model to assume the role of a banking agent. It explicitly tells the model to prioritize identifying the correct account names before proceeding with any balance inquiries or transactions. This is very important because users might use colloquial or imprecise names to refer to their accounts.\n",
    "\n",
    "Now that we have all components of the agent built, let's create the actual object."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Agent creation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To create the agent, we'll use the `langgraph` library introduced in the previous chapter. The `langgraph.prebuilt` module provides the create_react_agent function, which simplifies building the agent architecture. In the next chapter, we'll explore how to build cognitive architectures from scratch."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.prebuilt import create_react_agent\n",
    " \n",
    "agent = create_react_agent(\n",
    "    model=model, \n",
    "    tools=tools, \n",
    "    state_modifier=prompt_template\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the prompt is included using the `state_modifier` parameter. Besides a prompt template, this argument can be any `Runnable` that can be chained to the model and takes the state as an input."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The graph created by this function looks like the following figure:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "display(Image(agent.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Upon receiving input, the agent evaluates whether to generate a response directly or employ a tool. If a tool is deemed necessary, a loop is initiated. The output from the tool is then fed back into the model, prompting a reevaluation of the situation. This iterative process allows the agent to call any number of tools, as many times as required, before formulating a final response.\n",
    "\n",
    "Let's now use the agent we just created in different scenarios."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Agent usage"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "LangGraph primarily uses the `invoke` method to execute a graph. This method requires an initial state object containing a `\"messages\"` key. This key holds the conversation history between the user and the agent, encompassing human and agent messages, along with any intermediate tool calls and their corresponding responses. The `invoke` method processes this information, runs it through the grapht, and returns an updated state object, which includes any newly generated messages appended to the conversation history.\n",
    "\n",
    "Let's try it with the code snippet below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Hello, what can you help me with?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "I can help you check your account balances and transfer money between your accounts.\n"
     ]
    }
   ],
   "source": [
    "state = {\n",
    "    \"messages\": [\n",
    "        (\"human\", \"Hello, what can you help me with?\")\n",
    "    ]\n",
    "}\n",
    "\n",
    "new_state = agent.invoke(state)\n",
    "\n",
    "for message in new_state[\"messages\"]:\n",
    "    message.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we have asked a simple question to the agent, so no function calls are present in its response and it has just generated it without using any tools.\n",
    "\n",
    "Let's ask a specific question where we now the agent must utilize one of the provided tools such as asking an account balance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='Whats the balance in my savings account?', id='da34c3bf-1e7e-4bf8-b7e8-4cf81f3a5361'),\n",
       "  AIMessage(content='', additional_kwargs={'function_call': {'name': 'list_accounts', 'arguments': '{}'}}, response_metadata={'is_blocked': False, 'safety_ratings': [], 'usage_metadata': {'prompt_token_count': 124, 'candidates_token_count': 3, 'total_token_count': 127}, 'finish_reason': 'STOP'}, id='run-0b0f8f39-8aaf-4159-92ee-02e083b03ecf-0', tool_calls=[{'name': 'list_accounts', 'args': {}, 'id': 'f729594c-5479-4828-80c1-462d8639b40c', 'type': 'tool_call'}], usage_metadata={'input_tokens': 124, 'output_tokens': 3, 'total_tokens': 127}),\n",
       "  ToolMessage(content='[\"checking-account\", \"savings-account\"]', name='list_accounts', id='10c80591-a796-4bd6-9205-d5ba5ffe8b11', tool_call_id='f729594c-5479-4828-80c1-462d8639b40c'),\n",
       "  AIMessage(content='', additional_kwargs={'function_call': {'name': 'get_account_balance', 'arguments': '{\"account_name\": \"savings-account\"}'}}, response_metadata={'is_blocked': False, 'safety_ratings': [], 'usage_metadata': {'prompt_token_count': 141, 'candidates_token_count': 11, 'total_token_count': 152}, 'finish_reason': 'STOP'}, id='run-e0745881-db8b-47b5-aa53-29ae53376e4b-0', tool_calls=[{'name': 'get_account_balance', 'args': {'account_name': 'savings-account'}, 'id': '09ab9bd6-c0a5-495b-a063-fef67f775792', 'type': 'tool_call'}], usage_metadata={'input_tokens': 141, 'output_tokens': 11, 'total_tokens': 152}),\n",
       "  ToolMessage(content='$3000', name='get_account_balance', id='19e24fb3-958c-4cc5-b194-5357947a904e', tool_call_id='09ab9bd6-c0a5-495b-a063-fef67f775792'),\n",
       "  AIMessage(content='Your savings account has a balance of $3000.\\n', response_metadata={'is_blocked': False, 'safety_ratings': [], 'usage_metadata': {'prompt_token_count': 163, 'candidates_token_count': 14, 'total_token_count': 177}, 'finish_reason': 'STOP'}, id='run-16b2adb6-374b-4cfd-a1fe-719604aaa57d-0', usage_metadata={'input_tokens': 163, 'output_tokens': 14, 'total_tokens': 177})]}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state = {\n",
    "    \"messages\": [\n",
    "        (\"human\", \"Whats the balance in my savings account?\")\n",
    "    ]\n",
    "}\n",
    "\n",
    "agent.invoke(state)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As we can see in the output the agent has now made to function calls:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Make a succesful transaction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='Transfer 10$ from checking to savings please', id='f5b02a7d-ce1e-4aa3-98d9-161b9a68bc4a'),\n",
       "  AIMessage(content='', additional_kwargs={'function_call': {'name': 'list_accounts', 'arguments': '{}'}}, response_metadata={'is_blocked': False, 'safety_ratings': [], 'usage_metadata': {'prompt_token_count': 126, 'candidates_token_count': 3, 'total_token_count': 129}, 'finish_reason': 'STOP'}, id='run-cbeda685-8380-4072-886c-444871d8e2cf-0', tool_calls=[{'name': 'list_accounts', 'args': {}, 'id': 'b3aa4903-7f14-43d7-9a1b-209498fed3a9', 'type': 'tool_call'}], usage_metadata={'input_tokens': 126, 'output_tokens': 3, 'total_tokens': 129}),\n",
       "  ToolMessage(content='[\"checking-account\", \"savings-account\"]', name='list_accounts', id='a44680ab-7168-494b-8c8d-7a71bd8236e0', tool_call_id='b3aa4903-7f14-43d7-9a1b-209498fed3a9'),\n",
       "  AIMessage(content='', additional_kwargs={'function_call': {'name': 'transfer_money', 'arguments': '{\"destination_account\": \"savings-account\", \"source_account\": \"checking-account\", \"amount\": 10.0}'}}, response_metadata={'is_blocked': False, 'safety_ratings': [], 'usage_metadata': {'prompt_token_count': 143, 'candidates_token_count': 16, 'total_token_count': 159}, 'finish_reason': 'STOP'}, id='run-662dbd79-b4c9-43e6-b5e6-96eb2a35cd03-0', tool_calls=[{'name': 'transfer_money', 'args': {'destination_account': 'savings-account', 'source_account': 'checking-account', 'amount': 10.0}, 'id': 'a89812bf-aebe-47a8-8555-1b2853399cd2', 'type': 'tool_call'}], usage_metadata={'input_tokens': 143, 'output_tokens': 16, 'total_tokens': 159}),\n",
       "  ToolMessage(content='Successful transaction', name='transfer_money', id='2556b2cb-6b6a-49ed-9490-67c5331fc924', tool_call_id='a89812bf-aebe-47a8-8555-1b2853399cd2'),\n",
       "  AIMessage(content='OK. I have transferred $10 from your checking-account to your savings-account. Anything else?\\n', response_metadata={'is_blocked': False, 'safety_ratings': [], 'usage_metadata': {'prompt_token_count': 165, 'candidates_token_count': 23, 'total_token_count': 188}, 'finish_reason': 'STOP'}, id='run-60452526-1364-4537-8e5b-a82bda68788f-0', usage_metadata={'input_tokens': 165, 'output_tokens': 23, 'total_tokens': 188})]}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state = {\n",
    "    \"messages\": [\n",
    "        (\"human\", \"Transfer 10$ from checking to savings please\")\n",
    "    ]\n",
    "}\n",
    "\n",
    "agent.invoke(state)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Make an impossible transaction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [HumanMessage(content='Transfer 1,000$ from checking to savings please', id='0f592736-d5dc-4624-9f60-f565c9c26560'),\n",
       "  AIMessage(content='', additional_kwargs={'function_call': {'name': 'list_accounts', 'arguments': '{}'}}, response_metadata={'is_blocked': False, 'safety_ratings': [], 'usage_metadata': {'prompt_token_count': 129, 'candidates_token_count': 3, 'total_token_count': 132}, 'finish_reason': 'STOP'}, id='run-5d7457f4-819f-457a-8736-f4d2935c2813-0', tool_calls=[{'name': 'list_accounts', 'args': {}, 'id': 'b63c432a-4acf-49aa-ae07-8bd75e319cae', 'type': 'tool_call'}], usage_metadata={'input_tokens': 129, 'output_tokens': 3, 'total_tokens': 132}),\n",
       "  ToolMessage(content='[\"checking-account\", \"savings-account\"]', name='list_accounts', id='13f1766a-8093-441a-b1f5-87ea6b5f26a6', tool_call_id='b63c432a-4acf-49aa-ae07-8bd75e319cae'),\n",
       "  AIMessage(content='', additional_kwargs={'function_call': {'name': 'transfer_money', 'arguments': '{\"destination_account\": \"savings-account\", \"source_account\": \"checking-account\", \"amount\": 1000.0}'}}, response_metadata={'is_blocked': False, 'safety_ratings': [], 'usage_metadata': {'prompt_token_count': 146, 'candidates_token_count': 16, 'total_token_count': 162}, 'finish_reason': 'STOP'}, id='run-58aca0bc-6a7b-4949-90d3-f805ab941b3a-0', tool_calls=[{'name': 'transfer_money', 'args': {'destination_account': 'savings-account', 'source_account': 'checking-account', 'amount': 1000.0}, 'id': '5fcf72b7-6ccc-4ff5-9277-b75ef302f459', 'type': 'tool_call'}], usage_metadata={'input_tokens': 146, 'output_tokens': 16, 'total_tokens': 162}),\n",
       "  ToolMessage(content='There is not enough funds in account checking-account. The balance is only $90.0.', name='transfer_money', id='84ce6fd0-9a56-4488-b1be-6edb05515466', tool_call_id='5fcf72b7-6ccc-4ff5-9277-b75ef302f459'),\n",
       "  AIMessage(content='There is not enough funds in account checking-account. The balance is only $90.0.\\n', response_metadata={'is_blocked': False, 'safety_ratings': [], 'usage_metadata': {'prompt_token_count': 187, 'candidates_token_count': 22, 'total_token_count': 209}, 'finish_reason': 'STOP'}, id='run-99361556-8c55-400b-abcf-e2bff1d9f79a-0', usage_metadata={'input_tokens': 187, 'output_tokens': 22, 'total_tokens': 209})]}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state = {\n",
    "    \"messages\": [\n",
    "        (\"human\", \"Transfer 1,000$ from checking to savings please\")\n",
    "    ]\n",
    "}\n",
    "\n",
    "agent.invoke(state)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "More complex request"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "If my checking account have more than $50, transfer 30$ from checking to savings please.Then tell me start and the final balance in both accounts.\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  list_accounts (07e23126-8f89-4936-869b-7246f9bf9625)\n",
      " Call ID: 07e23126-8f89-4936-869b-7246f9bf9625\n",
      "  Args:\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: list_accounts\n",
      "\n",
      "[\"checking-account\", \"savings-account\"]\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  get_account_balance (630a6e29-d4d7-4e11-8618-318bfe9b0317)\n",
      " Call ID: 630a6e29-d4d7-4e11-8618-318bfe9b0317\n",
      "  Args:\n",
      "    account_name: checking-account\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: get_account_balance\n",
      "\n",
      "$90.0\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Your checking account has more than $50. I will transfer $30 from checking to savings.\n",
      "Tool Calls:\n",
      "  transfer_money (29d598dd-69e1-4c5d-8ce0-fc7d4a96ca8c)\n",
      " Call ID: 29d598dd-69e1-4c5d-8ce0-fc7d4a96ca8c\n",
      "  Args:\n",
      "    destination_account: savings-account\n",
      "    source_account: checking-account\n",
      "    amount: 30.0\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: transfer_money\n",
      "\n",
      "Successful transaction\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  get_account_balance (a3bbcf0c-0c4e-4689-be7d-a4cd6920417c)\n",
      " Call ID: a3bbcf0c-0c4e-4689-be7d-a4cd6920417c\n",
      "  Args:\n",
      "    account_name: checking-account\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: get_account_balance\n",
      "\n",
      "$60.0\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  get_account_balance (0adfbf23-0891-4416-be44-746e6581800c)\n",
      " Call ID: 0adfbf23-0891-4416-be44-746e6581800c\n",
      "  Args:\n",
      "    account_name: savings-account\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: get_account_balance\n",
      "\n",
      "$3040.0\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "The final balance in your checking account is $60.0. The final balance in your savings account is $3040.0.\n"
     ]
    }
   ],
   "source": [
    "state = {\n",
    "    \"messages\": [\n",
    "        (\"human\", \n",
    "            (\n",
    "            \"If my checking account have more than $50, transfer 30$ from checking to savings please.\" \n",
    "             \"Then tell me start and the final balance in both accounts.\"\n",
    "            )\n",
    "        )\n",
    "    ]\n",
    "}\n",
    "\n",
    "new_state = agent.invoke(state)\n",
    "for message in new_state[\"messages\"]:\n",
    "    message.pretty_print()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "If I have more than 100$ in my checking account, please transfer half of it to savings\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  list_accounts (f0422086-ee17-43e4-b2ed-4fbbed1923ac)\n",
      " Call ID: f0422086-ee17-43e4-b2ed-4fbbed1923ac\n",
      "  Args:\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: list_accounts\n",
      "\n",
      "[\"checking-account\", \"savings-account\"]\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  get_account_balance (b05a12fa-1687-46e9-a0c2-348120f97c2b)\n",
      " Call ID: b05a12fa-1687-46e9-a0c2-348120f97c2b\n",
      "  Args:\n",
      "    account_name: checking-account\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: get_account_balance\n",
      "\n",
      "$60.0\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "You only have $60.0 in your checking account, which is less than $100.0, so I will not transfer any money.\n"
     ]
    }
   ],
   "source": [
    "state = {\n",
    "    \"messages\": [\n",
    "        (\"human\", \"If I have more than 100$ in my checking account, please transfer half of it to savings\")\n",
    "    ]\n",
    "}\n",
    "\n",
    "new_state = agent.invoke(state)\n",
    "for message in new_state[\"messages\"]:\n",
    "    message.pretty_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Deploying an Agent in Reasoning Engine"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Collecting cloudpickle\n",
      "  Downloading cloudpickle-3.1.0-py3-none-any.whl.metadata (7.0 kB)\n",
      "Downloading cloudpickle-3.1.0-py3-none-any.whl (22 kB)\n",
      "Installing collected packages: cloudpickle\n",
      "Successfully installed cloudpickle-3.1.0\n",
      "\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m24.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.3.1\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "!pip install cloudpickle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "import vertexai\n",
    "from vertexai.preview import reasoning_engines\n",
    "\n",
    "\n",
    "PROJECT_ID = \"your-project-id\"\n",
    "LOCATION = \"us-central1\"\n",
    "STAGING_BUCKET = \"gs://your-staging-bucket-name\"\n",
    "\n",
    "\n",
    "vertexai.init(project=PROJECT_ID, location=LOCATION, staging_bucket=STAGING_BUCKET)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "class AgentApp:\n",
    "    \"\"\"Represents an application that can be deployed in Reasoning Engine\"\"\"\n",
    "    def __init__(self, project_id: str, location: str) -> None:\n",
    "        \"\"\"initializes the agent\"\"\"\n",
    "        self.project_id = project_id\n",
    "        self.location = location\n",
    "\n",
    "\n",
    "    def set_up(self):\n",
    "        \"\"\"Set ups the agent.\"\"\"\n",
    "        self._agent = agent\n",
    "                \n",
    "\n",
    "    def query(self, message: str) -> str:\n",
    "        \"\"\"Query the agent\"\"\"\n",
    "        state = {\"messages\": [(\"human\", message)]}\n",
    "        new_state = self._agent.invoke(state)\n",
    "        return new_state[\"messages\"][-1].content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Your savings account balance is $3040.0.\\n'"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "app = AgentApp(project_id=PROJECT_ID, location=LOCATION)\n",
    "app.set_up()\n",
    "app.query(\"What is my savings balance?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error (403 GET https://storage.googleapis.com/storage/v1/b/your-staging-bucket-name?projection=noAcl&prettyPrint=false: jzaldivar@google.com does not have storage.buckets.get access to the Google Cloud Storage bucket. Permission 'storage.buckets.get' denied on resource (or it may not exist).)\n",
      "This code won't work from IPython, check: https://github.com/pydantic/pydantic/issues/9698\n"
     ]
    }
   ],
   "source": [
    "# This code won't work when running in jupyter\n",
    "\n",
    "remote_agent = None\n",
    "\n",
    "try:\n",
    "    remote_agent = reasoning_engines.ReasoningEngine.create(\n",
    "        AgentApp(project_id=PROJECT_ID, location=LOCATION),\n",
    "        requirements=[\n",
    "            \"google-cloud-aiplatform[langchain,reasoningengine]\",\n",
    "            \"cloudpickle==3.0.0\",\n",
    "            \"pydantic==2.7.4\",\n",
    "            \"langgraph\",\n",
    "            \"httpx\",\n",
    "        ],\n",
    "        display_name=\"Banking account Agent\",\n",
    "        description=\"This is an agent that helps you managing your accounts\",\n",
    "        extra_packages=[],\n",
    "    )\n",
    "except Exception as error:\n",
    "    print(\n",
    "        f\"Error ({error})\\n\" \n",
    "        f\"This code won't work from IPython, check: https://github.com/pydantic/pydantic/issues/9698\"\n",
    "    )\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "if remote_agent:\n",
    "    remote_agent.query(message=\"What's the balance on my savings account?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "if remote_agent:\n",
    "    remote_agent.delete()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "https://github.com/GoogleCloudPlatform/generative-ai/blob/main/gemini/reasoning-engine/tutorial_langgraph.ipynb"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# API as a tool in Agent Builder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from fastapi import FastAPI\n",
    "\n",
    "app = FastAPI()\n",
    "\n",
    "@app.get(\"/list_accounts\")\n",
    "def list_accounts() -> str:\n",
    "    \"\"\"List the names of the user's accounts.\"\"\"\n",
    "    return USER_ACCOUNTS.get_account_names()\n",
    "\n",
    "\n",
    "@app.get(\"/get_account_balance\")\n",
    "def get_account_balance(account_name: str) -> str:\n",
    "    \"\"\"Get the balance of one of the user accounts by its exact name.\"\"\" \n",
    "    try:\n",
    "        account = USER_ACCOUNTS.get_account(account_name)\n",
    "        return f\"${account.balance}\"\n",
    "    except ValueError as error:\n",
    "        return f\"{error}\"\n",
    "\n",
    "\n",
    "@app.get(\"/transfer_money\")\n",
    "def transfer_money(amount: float, source_account: str, destination_account: str) -> str:\n",
    "    \"\"\"Transfer money between two accounts.\"\"\"\n",
    "    try:\n",
    "        USER_ACCOUNTS.transfer_money(amount, source_account, destination_account)\n",
    "        return \"Successful transaction\"\n",
    "    except ValueError as error:\n",
    "        return f\"{error}\""
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
